home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
COMMUNIC
/
1572B.ZIP
/
KMT_IBM4.ZIP
/
MSSCMD.ASM
next >
Wrap
Assembly Source File
|
1989-07-11
|
64KB
|
1,268 lines
NAME msscmd
; File MSSCMD.ASM
; Edit history:
; Last edit 21 Nov 1988
; 21 Nov 1988 Version 2.32
; 17 Oct 1988 Make command keyword search failure yield global kstatus of
; failure status, to inform Take/Macros of a defective command.
; 4 Aug 1988 Fix keyword count initialization in help display.
; 1 July 1988 Version 2.31
; 7 June 1988 Add comand.impdo flag to permit a keyword search failure to
; retry as a DO cmd (macro table). Used only by main Kermit command level.
; 27 May 1988 Allow "-<cr>" as line continuation pair and let "\-<cr>"
; stand for "-<end of line>". Add comand.cmblen to sense buffer overflow on
; calls to cmtxt; if comand.cmblen is left at zero then use 128 byte limit
; (comand.cmblen is cleared at command end).
; 15 May 1988 Make :label keywords no-ops, make query ordinary char in TAKEs.
; 6 May 1988 Show ambiguous keywords at parse time, do graceful interaction.
; 28 March 1988 Permit '\%x' (x = '0' or above) in commands as 'variables'
; (substituted string of text). Words are obtained from Macro table mcctab.
; DEF MAC and DO MAC define variables. DEF macro uses comand.cmper > 0 to
; allow '\%x'to be stored as a literal. Variables work in any command except
; DEF MAC. Major redesign of whole parser. [jrd]
; 4 March 1988 Rewrite keyword parsing to permit non-alphabetized tables
; and 8-bit characters. Add byte comand.cmwhite which when non-zero permits
; leading whitespace in cmtxt and cmfile commands; it is reset at command
; completion. Move procedure Prompt here from mssker. [jrd]
; 27 Feb 1988 Add capability of stdin being a file. [jrd]
; 1 Jan 1988 version 2.30
public comnd, comand, prserr
public intake, isdev, iseof, prompt, tolowr
include mssdef.h
datas segment public 'datas'
extrn flags:byte, trans:byte, taklev:byte, takadr:word, mcctab:byte
extrn kstatus:byte
comand cmdinfo <>
cmer00 db cr,lf,'?Program internal error, recovering$'
cmer01 db cr,lf,'?More parameters are needed$'
cmer02 db cr,lf,'?Word "$'
cmer03 db '" is not usable here$'
cmer04 db '" is ambiguous$'
cmer07 db cr,lf,'?Ignoring extra characters "$'
cmer08 db '"$'
cmer09 db cr,lf,'?Text exceeded available buffer capacity$'
cmin00 db ' Confirm with carriage return$'
cmin01 db ' One of the following:',cr,lf,'$'
stkmsg db cr,lf,bell,'?Exhausted work space! Circular definition?$'
crlf db cr,lf,'$'
ctcmsg db 5eh,'C$'
temp dw 0 ; temp (counts char/line so far)
errflag db 0 ; non-zero to suppress cmcfrm errors
kwstat db 0 ; get-keyword status
cmdstk dw 0 ; stack pointer at comand call time
intake db 0 ; last command line was from Take
prevch db 0 ; previous char read by cmgetc
noparse db 0 ; semicolons not special, if non-zero
subcnt db 0 ; count of chars matched in '\%'
datas ends
code segment public 'code'
extrn ctlu:near, cmblnk:near, clearl:near, locate:near, takrd:near
extrn takclos:near, docom:near
assume cs:code, ds:datas, es:nothing
; This routine parses the specified function in AH. Any additional
; information is in DX and BX.
; Returns rskp on success and ret on failure
COMND PROC NEAR
mov cmdstk,sp ; save stack ptr for longjmp exit
mov noparse,0 ; recognize semicolons in Take files
cmp ah,cmcfm ; Parse a confirm?
jne cm2 ; nz = no
jmp cmcfrm ; get a confirm
cm2: cmp ah,cmkey ; Parse a keyword?
jne cm3
jmp cmkeyw ; Try and get one
cm3: cmp ah,cmtxt ; Parse arbitrary text
jne cm4
jmp cmtext
cm4: cmp ah,cmfile ; parse text surrounded by whitespace
jne cm5
jmp cmfil0
cm5: mov ah,prstr ; Else give error
mov dx,offset cmer00 ; "?Program internal error"
int dos
jmp prserr
COMND ENDP
; This routine parses a keyword from the table pointed at by DX, help text
; point to by BX. Format of the table is as follows (use macro mkeyw):
; addr: db N ; Where N is the # of entries in the table
; db M ; M is the size of the keyword (excl '$')
; db 'string$' ; String is the keyword
; dw value ; Value is data to be returned
; Keywords may be in any order and in mixed case.
; Return is rskp for success and ret for failure.
; comand.cmptab: pointer to keyword table (supplied by caller)
; comand.cmhlp: pointer to help message (supplied by caller)
; comand.cmsptr: pointer to current user word text
; comand.cmsiz: length of user text, excluding terminator
; comand.cmcr: 0 = empty lines not allowed, 1 = empty lines allowed
; comand.cmwhite: non-zero allows leading whitespace for cmtxt and cmfile,
; reset automatically at end of call
; comand.cmwptr: buffer write pointer to next free byte
; comand.cmrptr: buffer read pointer for next free byte
; comand.cmper: 0 to do \%x substitution. Set to 0 at end of call
; comand.impdo: non-zero permits keyword failure to retry as DO command, reset
; automatically at time of failure.
cmkeyw proc near
mov comand.cmsiz,0 ; user word length
mov ax,comand.cmrptr ; get command reading pointer
mov comand.cmsptr,ax ; set pointer for start of user word
mov comand.cmhlp,bx ; save the help pointer
mov comand.cmptab,dx ; save the beginning of keyword table
mov bx,dx
cmp byte ptr[bx],0 ; get number of entries in table
jne cmky1
jmp cmky7 ; e = no keywords to check, error
cmky1: mov comand.cmsflg,0ffh ; skip leading spaces/tabs
call cmgtch ; get char from the user into ah
jc cmky3 ; c = terminator
mov dx,comand.cmrptr ; next byte to read
dec dx ; where we just read a char
mov comand.cmsptr,dx ; remember start of keyword
inc comand.cmsiz ; start counting user chars
cmky2: call cmgtch ; read until terminator
jc cmky3 ; c = terminator
inc comand.cmsiz ; count user chars
jmp short cmky2 ; no terminator yet
cmky3: cmp ah,'?' ; need help?
jne cmky4 ; ne = no
jmp cmkyhlp ; do help and exit
cmky4: cmp ah,escape ; escape?
jne cmky6 ; ne = no
call cmkyesc ; process escape
jc cmky5 ; c = failure (no unique keyword yet)
mov comand.cmper,0 ; reset to variable recognition
mov comand.cmkeep,0
mov comand.impdo,0 ; clear flag to prevent loops
mov comand.cmquiet,0 ; permit echoing again
jmp rskp ; return successfully to user
cmky5: cmp comand.cmsiz,0 ; started a word yet?
je cmky1 ; e = no, ignore escape, keep looking
jmp cmkyhlp ; ne = yes, show type of error
cmky6: cmp comand.cmsiz,0 ; length of user's text, empty?
je cmky7 ; e = yes, parse error
push bx
mov bx,comand.cmsptr ; point at first user character
cmp byte ptr[bx],':' ; start of a label?
pop bx
jne cmky6a ; ne = no, return success
mov comand.cmsiz,1 ; say just one byte
cmky6a:
call getkw ; get unique kw, point to it with bx
jc cmky8 ; c = not found
add bl,[bx] ; add length of keyword text (CNT)
adc bh,0
add bx,2 ; point at value field
mov bx,[bx] ; bx = return value following keyword
mov comand.cmper,0 ; reset to variable recognition
mov comand.cmkeep,0
mov comand.impdo,0 ; clear flag to prevent loops
mov comand.cmquiet,0 ; permit echoing again
mov errflag,0
jmp rskp ; return successfully
; all other terminators come here
cmky7: cmp comand.cmsiz,0 ; empty table or empty user's text?
jne cmky8 ; ne = no
cmp comand.cmcr,0 ; empty lines allowed?
jne cmky10 ; ne = yes, do not complain
push dx
mov ah,prstr
mov dx,offset cmer01 ; command word expected
int dos
pop dx
mov comand.cmquiet,0 ; permit echoing again
mov comand.impdo,0 ; clear flag to prevent loops
ret ; do ret exit
cmky8: cmp comand.impdo,0 ; failed here, ok to try Macro table?
je cmky8a ; e = no, use regular exit path
mov comand.impdo,0 ; yes, but clear flag to prevent loops
mov comand.cmrptr,offset comand.cmdbuf ; reinit read pointer
mov comand.cmquiet,1 ; suppress echoing of same keyword
mov bx,offset docom ; return DO as "found" keyword
jmp rskp ; return success to invoke DO
cmky8a: mov comand.cmquiet,0 ; permit echoing again
mov errflag,1 ; say already doing error recovery
or kstatus,1 ; global command status, failure
call isdev ; reading pretyped lines?
jnc cmky9 ; nc = yes, consume rest of line
cmp taklev,0 ; in a Take file?
jne cmky9 ; ne = yes
call cmskw ; display offending keyword
dec comand.cmrptr ; interactive, backup to terminator
mov bx,comand.cmrptr ; look at it
cmp byte ptr [bx],' ' ; got here on space terminator?
jne cmky10 ; ne = no, (cr,lf,ff) exit failure
mov ah,prstr ; start a fresh line
mov dx,offset crlf
int dos
call bufreset ; cut back buffer to just before term
jmp repars ; reparse interactive lines
cmky9: call cmcfrm ; get formal end of command line
nop ; to maintain illusion of typeahead
nop ; and let user backspace to correct
nop ; mistakes (we reparse everything)
call cmskw ; display offending keyword
cmky10: ret ; do ret exit
cmkeyw endp
;;;;;; start support routines for keyword parsing.
cmkyesc proc near ; deal with escape terminator
call bufreset ; reset buffer to end just before ESC
cmp comand.cmsiz,0 ; user word length, empty?
jne cmkye2 ; ne = have user text, else complain
cmkye1: call esceoc ; do normal escape end-of-command
stc ; say failure to fill out word
ret
; add unique keyword to buffer
cmkye2: call getkw ; is there a matching keyword?
jc cmkye1 ; c = ambiguous or not found
push bx ; unique, bx points to structure
push si
mov cl,[bx] ; length of keyword
mov ch,0
inc bx ; point to first letter
mov si,comand.cmwptr ; where next char goes
mov dx,comand.cmsiz ; length of user word
add bx,dx ; add chars known so far
sub cx,dx ; calculate number yet to add
add comand.cmsiz,cx
jcxz cmkye4 ; z = none
mov ah,conout ; display new char
cmkye3: mov al,[bx] ; get a keyword letter
inc bx
call tolowr ; lowercase
mov [si],al ; store it
inc si
mov dl,al
int dos ; display it
loop cmkye3 ; do all new chars
cmkye4: mov byte ptr[si],' ' ; insert space terminator in buffer
mov dl,' ' ; display it
mov ah,conout
int dos
inc si
mov comand.cmrptr,si ; move token pointer after the space
mov comand.cmwptr,si ; next free slot is after the space
mov comand.cmsflg,0ffh ; set space-seen flag
pop si
pop bx ; bx = keyword structure
add bl,[bx] ; add length of keyword text
adc bh,0
add bx,2 ; point at value field
mov bx,[bx] ; bx = return value following keyword
clc ; carry clear for success
ret
cmkyesc endp
esceoc proc near ; do normal escape end-of-command
push ax
push dx
mov ah,conout ; ring the bell
mov dl,bell
int dos
pop dx
pop ax
call bufreset ; reset buffer
stc ; say error condition
ret
esceoc endp
; Help. Question mark entered by user. Display all the keywords that match
; user text. If text is null then use external help if available; otherwise,
; display all keywords in the table. Removes question mark from buffer and
; invokes reparse of command line to-date. User word starts at .cmsptr and
; is .cmsiz bytes long.
cmkyhlp proc near
mov cx,0 ; clear number of keyword (none yet)
cmp comand.cmsiz,0 ; user text given?
jne cmkyh1 ; ne = yes, use matching keywords
cmp comand.cmhlp,0 ; external help given?
jne cmkyh6 ; yes, use it instead of full table
cmkyh1: mov temp,0 ; count # chars printed on this line
mov bx,comand.cmptab ; beginning of kw table
mov ch,[bx] ; length of table
mov cl,0 ; no keywords or help displayed yet
inc bx ; point at CNT field
cmkyh2: cmp comand.cmsiz,0 ; length of user word
je cmkyh3 ; e = null, use full table
call cmpwrd ; compare keyword with user word
jc cmkyh5 ; c = no match, get another keyword
cmkyh3: mov al,[bx] ; length of table keyword
add byte ptr temp,al ; count chars printed so far
cmp temp,76 ; will this take us beyond column 78?
jbe cmkyh4 ; be = no, line has more room
mov ah,prstr
mov dx,offset crlf ; break the line
int dos
mov temp,0 ; and reset the count
cmkyh4: cmp cl,0 ; any keywords found yet?
jne cmkyh4a ; ne = yes
mov dx,offset cmin01 ; start with One of the following: msg
mov ah,prstr
int dos
inc cl ; say one keyword has been found
cmkyh4a:mov dl,spc ; put two spaces before each keyword
mov ah,conout
int dos
int dos
add temp,2 ; count output chars
mov dx,bx ; get current keyword structure
inc dx ; text part
mov ah,prstr
int dos ; display it
cmkyh5: dec ch ; are we at end of table?
jle cmkyh7 ; le = yes, quit now
add bl,[bx] ; next keyword, add CNT chars to bx
adc bh,0
add bx,4 ; skip CNT, '$' and 16 bit value
jmp cmkyh2 ; go examine this keyword
cmkyh6: mov dx,comand.cmhlp ; external help text
mov ah,prstr
int dos
inc cl ; say gave help already
cmkyh7: cmp cl,0 ; found any keywords?
jne cmkyh9 ; ne = yes
mov cx,comand.cmsiz ; length of word
cmp cx,0
jg cmkyh8 ; g = something to show
push dx
mov ah,prstr
mov dx,offset cmer01 ; command word expected
int dos
pop dx
jmp prserr
cmkyh8: mov kwstat,0 ; set keyword not-found status
call cmskw ; display offending keyword
cmkyh9: mov ah,prstr ; start a fresh line
mov dx,offset crlf
int dos
call bufreset ; cut back buffer to just before '?'
jmp repars
cmkyhlp endp
; See if keyword is ambiguous or not from what the user has typed in.
; Return carry set if word is ambiguous or not found, carry clear otherwise.
; Uses table pointed at by comand.cmptab, user text pointed at by
; comand.cmsptr and length in comand.cmsiz.
cmambg proc near
push bx
push cx
push dx
mov dl,0 ; count keyword matches so far
mov bx,comand.cmptab ; look at start of keyword table
mov cl,[bx] ; get number of entries in table
mov ch,0 ; use cx as a counter
jcxz cmamb8 ; z = no table so always ambiguous
inc bx ; look at CNT byte of keyword
cmamb4: call cmpwrd ; user vs table words, same?
jc cmamb6 ; c = no match
inc dl ; count this as a match
cmp dl,1 ; more than one match?
ja cmamb8 ; a = yes, quit now
cmamb6: add bl,[bx] ; add CNT chars to bx
adc bh,0
add bx,4 ; skip CNT, '$' and 16 bit value
loop cmamb4 ; do rest of keyword table
cmamb7: cmp dl,1 ; how many matches were found?
jne cmamb8 ; ne = none or more than 1: ambiguous
pop dx ; restore main registers
pop cx
pop bx
clc
ret ; ret = not ambiguous
cmamb8: pop dx ; restore main registers
pop cx
pop bx
stc
ret ; return ambiguous or not found
cmambg endp
; Compare user text with keyword, abbreviations are considered a match.
; Enter with bx pointing at keyword table CNT field for a keyword.
; Return carry clear if they match, set if they do not match. User text
; pointed at by comand.cmsptr and length is in comand.cmsiz.
; Registers preserved.
cmpwrd proc near
push cx
mov cx,comand.cmsiz ; length of user's text
jcxz cmpwrd2 ; z: null user word matches no keyword
cmp cl,[bx] ; user's text longer than keyword?
ja cmpwrd2 ; a = yes, no match
push ax
push bx
push si
inc bx ; point at table's keyword text
mov si,comand.cmsptr ; buffer ptr to user input
cld
cmpwrd1:lodsb ; user text
mov ah,[bx] ; keyword text
inc bx ; next keyword letter
call tolowr ; force lower case on both chars
cmp ah,al ; same?
loope cmpwrd1 ; e = same so far
pop si
pop bx
pop ax
jne cmpwrd2 ; ne = mismatch
pop cx ; recover keyword counter
clc ; they match
ret
cmpwrd2:pop cx ; recover keyword counter
stc ; they do not match
ret
cmpwrd endp
; Get pointer to keyword structure using user text. Uses keyword table
; pointed at by comand.cmptab and comand.cmsiz holding length
; of user's keyword (cmpwrd needs comand.cmsptr pointing at user's
; keyword and length of comand.cmsiz). Structure pointer returned in BX.
; Return carry clear for success and carry set for failure. Modifies BX.
getkw proc near
push cx
mov kwstat,0 ; keyword status, set to not-found
cmp comand.cmsiz,0 ; length of user word, empty?
je getkw3 ; e = yes, fail
mov bx,comand.cmptab ; table of keywords
mov cl,[bx] ; number of keywords in table
mov ch,0
jcxz getkw3 ; z = none, fail
inc bx ; point to first
getkw1: call cmpwrd ; compare user vs table words
jc getkw2 ; c = failed to match word, try next
mov kwstat,1 ; say found one keyword, maybe more
push dx
mov dx,comand.cmsiz ; users word length
cmp [bx],dl ; same length (end of keyword)?
pop dx
je getkw4 ; e = yes, exact match. Done
call cmambg ; ambiguous?
jnc getkw4 ; nc = unique, done, return with bx
mov kwstat,2 ; say more than one such keyword
getkw2: add bl,[bx] ; next keyword, add CNT chars to bx
adc bh,0
add bx,4 ; skip CNT, '$' and 16 bit value
loop getkw1 ; do all, exhaustion = failure
getkw3: pop cx
stc ; return failure
ret
getkw4: pop cx
clc ; return success
ret
getkw endp
; show offending keyword message. Comand.cmsptr points to user word,
; comand.cmsiz has length. Modifies AX, CX, and DX.
cmskw proc near
mov ah,prstr ; not one of the above terminators
mov dx,offset cmer02 ; '?Word "'
int dos
mov cx,comand.cmsiz ; length of word
jcxz cmskw3 ; z = null
mov ah,conout
push si
mov si,comand.cmsptr ; point to word
cld
cmskw1: lodsb
cmp al,' ' ; control code?
jae cmskw2 ; ae = no
push ax
mov dl,5eh ; caret
int dos
pop ax
add al,'A'-1 ; plus ascii bias
cmskw2: mov dl,al ; display chars in word
int dos
loop cmskw1
pop si
cmskw3: mov dx,offset cmer03 ; '" not usable here.'
cmp kwstat,1 ; kywd status from getkw, not found?
jb cmskw4 ; b = not found, a = ambiguous
mov dx,offset cmer04 ; '" ambiguous'
cmskw4: mov ah,prstr
int dos
ret
cmskw endp
;;;;;;;;;; end of support routines for keyword parsing.
; Parse arbitrary text up to a CR. Enter with BX = pointer to output buffer,
; DX pointing to help text. Produces asciiz string. Return updated pointer in
; BX and output size in AH. Leading spaces are omitted unless comand.cmwhite
; is non-zero (cleared upon exit). It does not need to be followed by the
; usual call to confirm the line. Byte comand.cmblen can be used to specify
; the length of the caller's buffer; cleared to zero by this command to
; imply a length of 127 bytes (default) and if zero at startup use 127 bytes.
cmtext proc near
mov comand.cmptab,bx ; save pointer to data buffer
mov comand.cmhlp,dx ; save the help message
mov cx,0 ; init the char count
cmp comand.cmblen,0 ; length of user's buffer given?
jne cmtxt0 ; ne = yes
mov comand.cmblen,127 ; else set 127 byte limit plus null
cmtxt0: mov comand.cmsflg,0ffh ; skip initial spaces
cmp comand.cmwhite,0 ; allow leading whitespace?
je cmtxt1a ; e = no
cmtxt1: mov comand.cmsflg,0 ; get all spaces
cmtxt1a:call cmgtch ; get a char
jnc cmtxt5 ; nc = non-terminator, put in buffer
cmp ah,' ' ; space?
je cmtxt5 ; e = yes, record it
cmp ah,escape ; escape?
jne cmtxt2 ; ne = no
call esceoc ; do normal escape end-of-command
jmp cmtxt0 ; try again
cmtxt2: cmp ah,'?' ; asking a question?
je cmtxt3 ; e = yes
cmp ah,cr ; formal carriage return?
je cmtxt2a ; e = yes
inc comand.cmrptr ; accept char into buffer
jmp cmtxt5 ; and record the char
cmtxt2a:mov ah,cl ; return count in AH
mov bx,comand.cmptab ; return updated pointer
mov byte ptr[bx],0 ; put terminator into the buffer
mov comand.cmwhite,0 ; clear leading whitespace flag
mov comand.cmper,0 ; reset to variable recognition
mov comand.cmblen,0 ; set user buffer length to unknown
mov comand.cmkeep,0
jmp rskp
; Help processor
cmtxt3: inc comand.cmrptr ; count the ?
cmp cx,0 ; Is "?" first char?
jne cmtxt5 ; ne = no, just add to buffer
dec comand.cmrptr
mov comand.cmsiz,0 ; no keyword for help
mov comand.cmwhite,0 ; clear leading whitespace flag
cmp comand.cmhlp,0 ; external help given?
jne cmtxt3a ; ne = yes
mov comand.cmhlp,offset cmin00 ; confirm with c/r msg
mov comand.cmblen,0 ; set user buf length to unknown
cmtxt3a:jmp cmkyhlp ; do help process
cmtxt5: inc cx ; increment the count
mov bx,comand.cmptab ; pointer into destination array
mov [bx],ah ; put char into the buffer
inc bx
mov al,0
mov [bx],al ; insert null terminator
mov comand.cmptab,bx
cmp ch,0 ; overflowed into high byte?
jne cmtxt6 ; ne = yes
cmp cl,comand.cmblen ; buffer filled?
ja cmtxt6 ; a = yes, declare error
jb cmtxt5a ; a = not filled yet
mov ah,conout ; notify user that the buffer is full
mov dl,bell
int dos
cmtxt5a:jmp cmtxt1
cmtxt6: mov ah,prstr
mov dx,offset cmer09
int dos
jmp prserr ; declare parse error
cmtext endp
; Parse arbitrary text up to whitespace. Enter with DX pointing to output
; buffer and BX pointing to help text. Produces asciiz string. Return updated
; pointer in DX and input size in AH. Skips leading whitespace unless
; comand.cmwhite is non-zero (cleared upon exit). Does a return skip exit.
cmfil0 proc near
mov comand.cmptab,dx ; save pointer to data buffer
mov comand.cmhlp,bx ; save the help message
mov comand.cmsiz,0 ; init the char count
cmfil0a:cmp comand.cmwhite,0 ; allow leading whitespace?
jne cmfil1 ; ne = yes
mov comand.cmsflg,0ffh ; omit leading space
cmfil1: call cmgtch ; get a char
jc cmfi1a ; c = terminator
jmp cmfil5 ; put char into the buffer
cmfi1a: cmp ah,escape ; escape?
je cmfi1b ; e = yes
jmp cmfil2 ; process other terminators
cmfi1b: call esceoc ; do normal escape end-of-command
jmp cmfil0a ; try again
cmfil2: cmp ah,'?' ; asking a question?
je cmfil3 ; e = yes
xchg dx,bx ; re-interchange bx and dx
mov comand.cmwhite,0 ; clear whitespace flag
mov comand.cmper,0 ; reset to variable recognition
mov comand.cmkeep,0
mov bx,comand.cmptab ; pointer into destination array
mov byte ptr[bx],0 ; put null terminator into the buffer
inc bx
mov ah,byte ptr comand.cmsiz ; return count in AH
mov dx,comand.cmptab ; return updated pointer
xchg dx,bx ; re-interchange bx and dx
mov comand.cmwhite,0 ; clear whitespace flag
mov comand.cmper,0 ; reset to variable recognition
mov comand.cmkeep,0
jmp rskp ; return success
cmfil3: inc comand.cmrptr ; count the ?
cmp comand.cmsiz,0 ; Is "?" first char?
jne cmfil5 ; ne = no, just add to buffer
dec comand.cmrptr
mov comand.cmsiz,0
cmp comand.cmhlp,0 ; external help given?
jne cmfil3a ; ne = yes
mov comand.cmhlp,offset cmin00 ; confirm with c/r msg
cmfil3a:jmp cmkyhlp ; do help process
cmfil5: inc comand.cmsiz ; inrement the count
mov bx,comand.cmptab ; pointer into destination array
mov [bx],ah ; put char into the buffer
inc bx
mov comand.cmptab,bx
jmp cmfil1 ; the end of cmfil0
cmfil0 endp
; This routine gets a confirm (CR) and displays any extra non-blank text.
; errflag non-zero means suppress "extra text" display in this routine
; because another routine is handling errors.
cmcfrm proc near
mov comand.cmper,1 ; do not react to \%x substitutions
cmcfr1: mov comand.cmsflg,0ffh ; set space-seen flag (skip spaces)
call cmgtch ; get a char
push comand.cmrptr
pop temp ; remember first non-space position
jc cmcfr4 ; c = terminator
dec temp ; backup to text char
cmcfr3: mov comand.cmsflg,0ffh ; set space-seen flag (skip spaces)
call cmgtch
jnc cmcfr3 ; read until terminator
cmcfr4: cmp ah,' '
je cmcfr3 ; ignore ending on space
cmp ah,escape ; escape?
jne cmcfr5 ; ne = no
call esceoc ; do standard end of cmd on escape
mov ax,comand.cmrptr
cmp ax,temp ; started text yet?
je cmcfr1 ; e = no
jmp short cmcfr3 ; try again
cmcfr5: cmp ah,'?' ; curious?
jne cmcfr6 ; ne = no
mov comand.cmhlp,offset cmin00 ; msg Confirm with c/r
mov comand.cmsiz,0 ; no keyword
mov errflag,0
jmp cmkyhlp ; do help
cmcfr6: cmp ah,cr ; the confirmation char?
jne cmcfr3 ; ne = no
cmp errflag,0 ; already doing one error?
jne cmcfr7 ; ne = yes, skip this one
mov cx,comand.cmrptr ; pointer to terminator
mov dx,temp ; starting place
sub cx,dx ; end minus starting point = length
cmp cx,0 ; string present?
jle cmcfr7 ; le = nothing to display
push dx ; save source pointer
mov ah,prstr
mov dx,offset cmer07 ; ?Ignoring extras
int dos
pop dx
mov bx,1 ; stdout handle, cx=count, dx=src ptr
mov ah,write2 ; allow embedded dollar signs
int dos
mov ah,prstr
mov dx,offset cmer08 ; trailer msg
int dos
cmcfr7: mov errflag,0
mov comand.cmper,0 ; reset to variable recognition
mov comand.cmkeep,0
jmp rskp ; return confirmed
cmcfrm endp
;;; Routines to get and edit incoming text.
; Detect '\%x' (x = '0' or above) and substitute the matching Macro string
; in place of the '\%x' phrase in the user's buffer. If comand.cmper != 0
; then treat '\%' as literal characters. If no matching parameter exists
; just remove '\%x'. Returns carry clear if nothing done, else carry set and
; new text already placed in user's buffer. comand.cmwptr and comand.cmcnt
; are updated. Uses depth-first recursion algorithm. All registers preserved.
subst proc near
cmp comand.cmper,0 ; should we recognize '\%'?
jne subst2 ; ne = no, treat as literals
cmp ah,'\' ; is it the first char of the pattern?
jne subst1 ; ne = no, try next
mov subcnt,1 ; say first is matched
jmp short subst2 ; exit successfully
subst1: cmp subcnt,1 ; first char matched already?
ja subst3 ; a = first two have been matched
jb subst2 ; b = none yet
inc subcnt ; assume a match follows
cmp ah,'%' ; second match char, same?
je subst2 ; e = yes
mov subcnt,0 ; mismatch, clear match counter
subst2: clc ; carry clear = no substitution done
ret
subst3: mov subcnt,0 ; clear match counter
cmp ah,'0' ; third char is '0' or above?
jb subst2 ; b = out of range, no match
push bx ; save working regs
push cx
sub comand.cmrptr,3 ; reread commands where backslash was
call bufreset ; reset buffer to this point
push comand.cmptab ; save current keyword parsing parms
push comand.cmsptr
push comand.cmsiz
mov bx,comand.cmrptr ; points at backslash
mov comand.cmsptr,bx ; direct keyword routine to it
mov comand.cmsiz,3 ; three bytes (\%x) of user text
mov comand.cmptab,offset mcctab ; use Macro table for new text
call getkw ; get ptr, bx, to matching keyword
pop comand.cmsiz ; restore borrowed keyword parameters
pop comand.cmsptr
pop comand.cmptab
jc substx ; c = not found, keep after pops
mov cl,byte ptr[bx] ; length of found word
add cl,2 ; plus count field and '$'
mov ch,0
add bx,cx ; point to 16 bit value (string ptr)
mov bx,[bx] ; point to string structure
mov cl,[bx] ; length of string (ch=0 from above)
inc bx ; skip length byte, bx=string address
jcxz substx ; z = nothing left to transfer
cld
subst4: mov ah,[bx] ; get a char
inc bx
push di ; assume di is used by other routines
mov di,comand.cmrptr
mov [di],ah ; store char (without es:di)
inc di
mov comand.cmwptr,di ; where to store next char
mov comand.cmrptr,di ; move read pointer too
cmp di,offset comand.cmdbuf+size cmdbuf ; reached max buffer size?
pop di
jae subst6 ; ae = yes, no more room
cmp sp,20*2 ; still some stack space?
jle subst6 ; le = insufficient room
call SUBST ; rescan what we stored (recursion)
jnc subst5 ; nc = no substitution, so no kbd test
push ax ; break out of loops with Control-C
push dx
mov ah,constat ; check console status for Control-C
int dos ; our control-break handler sees it
pop dx ; and cmgetc reads it
pop ax
subst5: loop subst4
substx: pop cx
pop bx
stc ; set carry to say have stored chars
ret
subst6: mov ah,prstr
mov dx,offset stkmsg ; out of work space msg
int dos
jmp prserr ; and declare parse error
subst endp
; Read chars from Take file, keyboard, or redirected stdin. Edit and remove
; BS & DEL, Tab becomes space, act on Control-C, pass Control-U and Control-W.
; Do echoing unless comand.cmquiet is non-zero. Do semicolon comments in Take
; and indirect stdin files (\; means literal semicolon). Return char in AL.
CMGETC proc near ; Basic raw character reader
mov ah,taklev ; get current Take level
mov intake,ah ; remember here for later callers
cmget01:cmp prevch,0 ; left over char yet to be exported?
je cmget02 ; e = no
mov al,prevch ; get old char
mov prevch,0 ; clear storage
jmp cmget6 ; analyze it
cmget02:cmp taklev,0 ; in a Take file?
jne cmget1 ; ne = yes, do Take reader section
call isdev ; is stdin a device or a file?
jnc cmget20 ; nc = file (redirection of stdin)
jmp cmget10 ; c = device, do separately
cmget20:call iseof ; see if file is empty
jc cmget21 ; c = EOF on disk file
mov ah,coninq ; read the char from file, not device
int dos
cmp al,cr ; is it a cr?
je cmget01 ; yes, ignore and read next char
cmp al,ctlz ; Control-Z?
je cmget21 ; e = yes, same as EOF here
cmp al,lf ; LF's end lines from disk files
jne cmget23 ; ne = not LF, pass along as is
mov al,cr ; make LF a CR for this parser
call iseof ; see if this is the last char in file
jnc cmget23 ; nc = not EOF, process new CR
cmget21:mov flags.extflg,1 ; EOF on disk file, set exit flag
cmget23:jmp short cmget6 ; do echoing and return
cmget1: push bx ; read from Take file
mov bx,takadr
cmp [bx].takcnt,0 ; bytes remaining in Take buffer
jne cmget4 ; ne = not empty
cmp [bx].taktyp,0feh ; type of Take (file?)
jne cmget3 ; ne = no (macro)
call takrd ; read another buffer
cmp [bx].takcnt,0 ; anything in the file?
jne cmget4 ; ne = yes
cmget3: pop bx ; clear stack
jmp cmget5 ; close take file
cmget4: push si
mov si,[bx].takptr ; read a char from Take buffer
cld
lodsb
mov [bx].takptr,si ; move buffer pointer
pop si
dec [bx].takcnt ; decrease number of bytes remaining
pop bx
cmp al,ctlz ; Control-Z?
jne cmget6 ; ne = no
cmget5: cmp comand.cmkeep,0 ; keep Take/macro open after eof?
jne cmget5a ; ne = yes
call takclos ; close take file
cmget5a:mov al,cr ; report cr as last char
mov noparse,0 ; and say end of comment
; start common code
cmget6: cmp al,lf ; line feed?
jne cmget8 ; ne = no
jmp cmget01 ; yes, ignore and read another char
; handle comments (echo but not parse)
cmget8: cmp noparse,0 ; parsing?
jne cmget9 ; ne = yes, do echo and no parse
cmp prevch,0 ; have previous char to analyze?
jne cmget8c ; ne = yes
cmp al,'\' ; start of '\;'?
jne cmget8a ; ne = no
mov prevch,al ; yes, maybe. save '\' til later
jmp cmget02 ; read next char
cmget8a:cmp al,';' ; possible start of comment?
jne cmget8e ; no, export al
mov al,' ' ; replace ';' with space for comment
jmp cmget9 ; go start a comment
cmget8c:cmp al,';' ; end of '\;'?
je cmget8d ; e = yes, omit leading backslash
xchg prevch,al ; no, save new, recover old '\'
jmp short cmget8e ; export '\'
cmget8d:mov prevch,0 ; clear old '\'
cmget8e:jmp cmget12 ; do parsing
mov prevch,0 ; clear previous char
jmp cmget02 ; get next char
; echo comment
cmget9: cmp flags.takflg,0 ; echoing take files?
je cmget9a ; e = no
push ax
push dx
mov ah,conout ; echo current char
mov dl,al
int dos
pop dx
pop ax
cmget9a:mov noparse,0 ; cr ends comment
cmp al,cr ; end of comment?
je cmget13 ; e = yes, export cr
mov noparse,1 ; still in comment
jmp cmget01 ; read more chars
; read from tty device
cmget10:mov ah,coninq ; Get a char from device, not file
int dos ; with no echoing
or al,al
jnz cmget12 ; ignore null bytes of special keys
int dos ; read and discard scan code byte
jmp cmget10 ; try again
cmget12:cmp al,'C'and 1Fh ; Control-C?
je cmget14 ; e = yes
cmp al,TAB ; tab is replaced by space
jne cmget13 ; ne = not tab
mov al,' '
cmget13:ret ; normal exit, char is in AL
cmget14:mov ah,prstr ; Control-C handler
push dx
mov dx,offset ctcmsg ; show Control-C
int dos
pop dx
mov prevch,0
mov flags.cxzflg,'C' ; tell others the news
mov sp,cmdstk ; restore command entry stack pointer
ret ; and fail immediately (a longjmp)
cmgetc endp
; Read chars from user (cmgetc). Detect terminators. Reads from buffer
; comand.cmbuf. Set read pointer comand.cmrptr to next free buffer byte if
; char is not a terminator: chars CR, LF, FF, '?' (returns carry set for
; terminators). Do ^U, ^W editing, convert FF to CR plus clear screen.
; Edit "-<cr>" as line continuation, "\-<cr>" as "-<end of line>".
; Return char in AH.
CMINBF proc near ; Buffer reader, final editor
push di
mov di,comand.cmwptr
cmp di,offset comand.cmdbuf+size cmdbuf-3 ; max buffer size - 3
pop di
jb cminb1 ; b = not full
mov ah,conout ; almost full, notify user
push dx
mov dl,bell
int dos
mov dx,offset comand.cmdbuf+size cmdbuf
cmp comand.cmrptr,dx ; max buffer size?
pop dx
jb cminb1 ; b = more room
mov ah,prstr
push dx
mov dx,offset cmer09 ; command too long
int dos
pop dx
jmp prserr ; overflow = parse error
cminb1: push bx
mov bx,comand.cmrptr
mov ah,[bx] ; get current command char while here
cmp bx,comand.cmwptr ; do we need to read more?
pop bx ; no if cmrptr < cmwptr
jb cminb2 ; b: cmrptr < cmwptr (have extra here)
call cmgetc ; no readahead, read another into al
mov ah,al ; keep char in 'ah'
push bx
mov bx,comand.cmwptr ; Get the pointer into the buffer
mov [bx],ah ; Put it in the buffer
inc bx
mov comand.cmwptr,bx ; inc write pointer
pop bx
; Char to be delivered is in ah
cminb2: cmp ah,'W' and 1fh ; Is it a ^W?
jne cminb3
call cntrlw ; Kill the previous word
jnc cminbf ; nc = no change, get another char
jmp repars ; need a new command scan (cleans stk)
cminb3: cmp ah,'U' and 1fh ; Is it a ^U?
jne cminb3a ; ne = no
mov comand.cmwptr,offset comand.cmdbuf ;reset buffer write pointer
jmp repars ; Go start over (cleans stack)
; BS and DEL
cminb3a:cmp ah,DEL ; Delete code?
je cminb3b ; e = yes
cmp ah,BS ; Backspace (a delete operator)?
jne cminb4 ; ne = no
cminb3b:call bufdel ; delete char from buffer
jc cminb3c ; c = did erasure
jmp cminbf ; no erasure, ignore BS, get more
cminb3c:jmp repars ; could have deleted previous token
cminb4: push bx ; look for hyphen or \hyphen
cmp ah,cr ; check for hyphen line continuation
jne cminb4b ; ne = not end of line
mov bx,comand.cmwptr ; Get the pointer into the buffer
cmp bx,offset comand.cmdbuf-2 ; do we have a previous char?
jb cminb4b ; b = no
cmp byte ptr[bx-2],'-' ; previous char was a hyphen?
jne cminb4b ; ne = no
pop bx
call bufdel ; delete the hyphen
jmp repars
cminb4b:pop bx
; Echoing done here
cmp comand.cmquiet,0 ; quiet mode?
jne cminb5 ; yes, skip echoing
cmp taklev,0 ; in a take file?
je cminb4a ; e = no
cmp flags.takflg,0 ; echo take file?
je cminb5 ; e = no
cminb4a:push ax ; save the char
cmp ah,' ' ; printable?
jae cminb4c ; yes, no translation needed
cmp ah,cr ; this is printable
je cminb4c
cmp ah,lf
je cminb4c
cmp ah,escape ; escape?
je cminb4d ; do not echo this character
push ax ; show controls as caret char
push dx
mov dl,5eh ; caret
mov ah,conout
int dos
pop dx
pop ax
add ah,'A'-1 ; make control code printable
cminb4c:push dx
mov dl,ah
mov ah,conout
int dos ; echo it ourselves
pop dx
cminb4d:pop ax ; and return char in ah
cminb5: cmp ah,cr ; Is it a carriage return?
je cminb6
cmp ah,lf ; Is it a line feed?
je cminb6
cmp ah,ff ; Is it a formfeed?
jne cminb7 ; none of the above, report bare char
call cmblnk ; FF: clear the screen and
push bx
push cx
push dx
call locate ; Home the cursor
mov bx,comand.cmwptr ; make the FF parse like a cr
mov byte ptr [bx-1],cr ; pretend a carriage return were typed
pop dx
pop cx
pop bx
cminb6: cmp comand.cmwptr,offset comand.cmdbuf ; parsed any chars yet?
jne cminb7 ; ne = yes
cmp comand.cmcr,0 ; bare cr's allowed?
jne cminb7 ; ne = yes
jmp prserr ; If not, just start over
cminb7: clc
ret
cminbf endp
; Read chars from cminbf. Comand.cmrptr points to next char to be read.
; Compresses repeated spaces if comand.cmsflg is non-zero. Exit with
; comand.cmrptr pointing at a terminator or otherwise at next free slot.
; Non-space then space acts as a terminator but comand.cmrptr is incremented.
; Substitution variables, '\%x', are detected and expanded. Return char in AH.
CMGTCH proc near ; return char in AH, from rescan buf
call cminbf ; get char from buffer or user
push bx
mov bx,comand.cmrptr ; get read pointer into the buffer
mov ah,[bx] ; read the next char
inc bx
mov comand.cmrptr,bx ; where to read next time
pop bx
call subst ; examine for text substitution
jnc cmgtc1 ; nc = no substitutions done
jmp repars ; reparse line with new material
cmgtc1: cmp ah,' ' ; Is it a space?
jne cmgtc3 ; ne = no
cmgtc2: cmp comand.cmsflg,0 ; space flag, was last char a space?
jne cmgtch ; ne = yes, get another char
mov comand.cmsflg,0FFH ; Set the space(s)-seen flag
mov ah,' ' ; character for caller
stc ; set carry for terminator
ret ; return space as a terminator
cmgtc3: mov comand.cmsflg,0 ; clear the space-seen flag
cmp ah,escape ; terminators remain in buffer but
je cmgtc4 ; are ready to be overwritten
cmp ah,'?' ; Is the user curious?
jne cmgtc3a ; ne = no
cmp taklev,0 ; in a Take file?
jne cmgtc3b ; ne = yes, make query ordinary char
je cmgtc4
cmgtc3a:cmp ah,cr
je cmgtc4
cmp ah,lf
je cmgtc4
cmp ah,ff
je cmgtc4
cmgtc3b:clc ; carry clear for non-terminator
ret
cmgtc4: dec comand.cmrptr ; point at terminating char
stc ; set carry to say it is a terminator
ret
cmgtch endp
; Reset comand.cmdbuf write pointer (.cmwptr) to where the read pointer
; (.cmrptr) is now. Discards material not yet read.
bufreset proc near
push comand.cmrptr ; where next visible char is read
pop comand.cmwptr ; where new char goes in buffer
ret
bufreset endp
; Delete character from screen and adjust buffer. Returns carry clear if
; no erasure, carry set otherwise.
bufdel proc near
dec comand.cmrptr ; remove previous char from buffer
cmp comand.cmrptr,offset comand.cmdbuf ; back too far?
jae bufde2 ; ae = no, material can be erased
mov comand.cmrptr,offset comand.cmdbuf ; set to start of buffer
call bufreset ; reset buffer
clc ; say no erasure
ret
bufde2: call bufreset ; reset buffer
stc ; say did erasure
ret
bufdel endp
; Come here is user types ^W when during input. Remove word from buffer.
cntrlw proc near
push ax
push cx
push dx
mov cx,comand.cmrptr ; char beyond what user sees
mov comand.cmwptr,cx ; truncate buffer there
sub cx,offset comand.cmdbuf ; compute chars in buffer
clc ; say have not yet modified line
jcxz ctlw2 ; z = nothing to do, exit no-carry
push es
std ; scan backward
mov ax,ds
mov es,ax ; point to the data are
mov di,comand.cmwptr ; looking from here
dec di
mov al,' '
repe scasb ; look for non-space
je ctlw1 ; all spaces, nothing to do
inc di ; move back to non-space
inc cx
repne scasb ; look for a space
jne ctlw1 ; no space, leave ptrs alone
inc di
inc cx ; skip back over space
ctlw1: inc di
pop es
cld ; reset direction flag
mov comand.cmwptr,di ; update pointer
stc ; set carry to say modified line
ctlw2: pop dx
pop cx
pop ax
ret
cntrlw endp
; Jump to REPARS to do a rescan of the existing buffer.
; Jump to PRSERR on a parsing error (quits command, clears old read material)
PRSERR PROC NEAR
mov comand.cmwptr,offset comand.cmdbuf ; initialize write pointer
mov ah,prstr
mov dx,offset crlf ; leave old line, start a new one
int dos
; reparse current line
REPARS: mov comand.cmrptr,offset comand.cmdbuf ; reinit read pointer
mov comand.cmper,0 ; reset to variable recognition
mov comand.cmsflg,0FFH ; strip leading spaces
cmp taklev,0 ; in Take cmd?
je prser2 ; e = no
cmp flags.takflg,0 ; echo contents of Take file?
je prser3 ; e = no
prser2: call ctlu ; clear display's line, reuse it
mov ah,prstr
mov dx,comand.cmprmp ; display the prompt
int dos
prser3: mov bx,0ffffh ; returned keyword value
mov sp,comand.cmostp ; set new sp to old one
jmp comand.cmrprs ; jump to just before the prompt call
PRSERR ENDP
; This routine prints the prompt and specifies the reparse address.
; Enter with pointer to prompt string in dx.
PROMPT PROC NEAR
mov comand.cmprmp,dx ; save the prompt
pop ax ; Get the return address
mov comand.cmrprs,ax ; Save as address to go to on reparse
mov comand.cmostp,sp ; Save for later restoration
push ax ; Put it on the stack again
mov ax,offset comand.cmdbuf
mov comand.cmwptr,ax ; reset buffer read/write pointers
mov comand.cmrptr,ax
mov ax,0
mov comand.cmper,0 ; allow substitutions
mov comand.cmsflg,0FFH ; remove leading spaces
cmp flags.takflg,0 ; look at Take flag
jne promp1 ; ne=supposed to echo, skip this check
cmp taklev,0 ; inside a take file?
je promp1 ; no, keep going
ret ; yes, return
promp1: mov ah,prstr
mov dx,offset crlf
int dos
mov ah,prstr ; display the prompt
mov dx,comand.cmprmp
int dos
ret
PROMPT ENDP
ISDEV PROC NEAR ; Set carry if STDIN is non-disk
push ax
push bx
push dx
mov al,0 ; get device info
mov ah,ioctl
mov bx,0 ; handle 0 is stdin
int dos
and dl,80h ; bit set if handle is for a device
rcl dl,1 ; put it into the carry bit
pop dx
pop bx
pop ax
ret ; carry set if device
ISDEV ENDP
ISEOF PROC NEAR ; Set carry if STDIN is at EOF
push ax ; but only if stdin is a non-device
push bx
push dx
mov al,0 ; get device info
mov ah,ioctl
mov bx,0 ; handle 0 is stdin
int dos
test dl,80h ; bit set if handle is for a device
mov ah,ioctl
mov al,6 ; get handle input status, set al
jnz iseof1 ; nz = device, always ready (al != 0)
int dos
iseof1: or al,al ; EOF?
pop dx
pop bx
pop ax
jnz iseof2 ; nz = no
stc ; set carry for eof
ret
iseof2: clc ; clear carry for not-eof
ret
ISEOF ENDP
; Convert ascii characters in al and ah to lowercase. [jrd]
; All registers are preserved except AX, of course.
TOLOWR PROC NEAR
cmp ah,'A' ; less that cap A?
jl tolow1 ; l = yes. leave untouched
cmp ah,'Z'+1 ; more than cap Z?
jns tolow1 ; ns = yes
or ah,20H ; convert to lowercase
tolow1: cmp al,'A' ; less that cap A?
jl tolow2 ; l = yes. leave untouched
cmp al,'Z'+1 ; more than cap Z?
jns tolow2 ; ns = yes
or al,20H ; convert to lowercase
tolow2: ret
TOLOWR endp
; Jumping to this location is like retskp. It assumes the instruction
; after the call is a jmp addr.
RSKP PROC NEAR
pop bp
add bp,3
push bp
ret
RSKP ENDP
; Jumping here is the same as a ret.
R PROC NEAR
ret
R ENDP
code ends
end